from datetime import datetime
from elasticsearch import Elasticsearch
import collections


#query: the query string e.g. 'bill OR Gates AND (vista OR XP) AND NOT mac'
query='bill OR Gates AND (vista OR XP) NOT mac'
offSet=1
page_size=10
time_range='2022/01/01-2022/12/31'
time_filter = [dt.strptime(x, "%Y/%m/%d") for x in time_range.split("-")]
categories=['life','culture','travel']
sources=["usatoday","chinadaily",'apnews']#注意这儿要小写的


es = Elasticsearch("http://localhost:9200")


def shunting_yard(infix_tokens):
    '''
    returns the list of postfix tokens converted from the given infix expression
    params:
        infix_tokens: list of tokens in original query of infix notation
    '''
    # define precedences
    precedence = {}
    precedence['NOT'] = 3
    precedence['AND'] = 2
    precedence['OR'] = 1
    precedence['('] = 0
    precedence[')'] = 0    

    # declare data strucures
    output = []
    operator_stack = []

    # while there are tokens to be read
    for token in infix_tokens:        
        # if left bracket
        if (token == '('):
            operator_stack.append(token) 
        # if right bracket, pop all operators from operator stack onto output until we hit left bracket
        elif (token == ')'):
            operator = operator_stack.pop()
            while operator != '(':
                output.append(operator)
                operator = operator_stack.pop()        
        # if operator, pop operators from operator stack to queue if they are of higher precedence
        elif (token in precedence):
            # if operator stack is not empty
            if (operator_stack):
                current_operator = operator_stack[-1]
                while (operator_stack and precedence[current_operator] > precedence[token]):
                    output.append(operator_stack.pop())
                    if (operator_stack):
                        current_operator = operator_stack[-1]

            operator_stack.append(token) # add token to stack       
        # else if operands, add to output list
        else:
            output.append(token.lower())

    # while there are still operators on the stack, pop them into the queue
    while (operator_stack):
        output.append(operator_stack.pop())
    # print ('postfix:', output)  # check
    return output

fd="content"
mode="match"

#query:  the query string e.g. 'bill OR Gates AND (vista OR XP) AND NOT mac'
query='bill OR Gates AND (vista OR XP) NOT mac'
query = query.replace('(', '( ')
query = query.replace(')', ' )')
query = query.split(' ')
postfix_queue1 = collections.deque(shunting_yard(query)) # get query in postfix notation as a queue
postfix_queue = collections.deque([])
#print(postfix_queue1)
results_stack=[]
result={}

while postfix_queue1:
  i=postfix_queue1.popleft()
  if i!='AND' and i!='OR' and i!='NOT':
    postfix_queue.append({mode:{fd:i}})
  else:
    postfix_queue.append(i)

timerange={"range":{"time":{"gte": time_filter[0],"lte":time_filter[1]}}}
postfix_queue.appendleft(timerange)
postfix_queue.append('AND')

categories={"terms":{"category":categories}}
postfix_queue.appendleft(categories)
postfix_queue.append('AND')

sources={"terms":{"source":sources}}
postfix_queue.appendleft(sources)
postfix_queue.append('AND')

#print(postfix_queue)
  
while postfix_queue:
    token=postfix_queue.popleft()
    if (token != 'AND' and token != 'OR' and token != 'NOT'):
        results_stack.append(token)    
    elif (token == 'AND'):
        right=results_stack.pop()
        left=results_stack.pop()
        result={"bool":{"must":[right,left]}}
        results_stack.append(result)
    elif (token == 'OR'):
        right=results_stack.pop()
        left=results_stack.pop()
        result={"bool":{"should":[right,left]}}
        results_stack.append(result)     
    elif (token == 'NOT'):
        right=results_stack.pop()
        left=results_stack.pop()
        result={"bool":{"must_not":[right,left]}}
        results_stack.append(result)
#print(postfix_queue1)

#按一个socre排序返回结果，应该是相关性排序~
#body = {"query":results_stack[0]}
#高亮，额外增加参数可以显示高亮内容，如 "highlight": ...<em>bill</em>...，具体细节可以print(resp)查看
#还可以加更多参数，如控制从第几项开始返回，返回多少项,适合分页
#body={"query":results_stack[0],"highlight": {"fields": {"content": {}}},"size": 100,"from": 10}

body={"query":results_stack[0],"highlight": {"fields": {"content": {}}},"size": page_size,"from": offSet}

#print(body)
resp = es.search(index="news", body=body) 

cand_list=[]
for i in resp['hits']['hits']:
    cand_list.append(i['_source'])

#print(cand_list)

#print(resp)
#print("Got %d Hits:" % resp['hits']['total']['value'])
#for hit in resp['hits']['hits']:
    #print("score:"+str(hit['_score']))
    #print("%(time)s %(source)s %(category)s: %(title)s" % hit["_source"])
